iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 21
3
AI & Data

Machine Learning for Dummies系列 第 21

[Day 21] 實作梯度下降法

  • 分享至 

  • xImage
  •  

上週學習完了基本工具的使用,今天開始我們要嘗試用梯度下降法來解決問題!

問題回顧

阿鐵想要買一輛新車,但是想知道這台車的耗油程度如何,除了看看車商數據(拿來廣告用的理想數據)之外,阿鐵也想從不同的車主身上實際去了解這台車的耗油程度,於是阿鐵就在論壇上發文詢問,很幸運的有 10 位好心車主分享了他們的油耗與里程數,資料如下

油耗(公升) 里程(KM)
9 45
4 24
7 59
3 28
10 91
7 28
4 42
3 18
1 14
9 82

要如何找到「油耗」與「里程」之間的對應關係,來幫助阿鐵預測未來每公升的油能跑的公里數呢?這時候我們就用迴歸分析來解決這個問題!

決定模型

這裡我們選擇最簡單也最常見的模型,就是線性模型

y = a * x + b

在圖上要畫出一條線有很多種可能,那麼我們要如何找到最好的那條線呢?換句話說,我們要如何找到一組 a & b,可以讓預測值與實際值之間的差距最小呢?

Imgur

要找到那條線,也就是說,找到讓損失函數 (Loss Function)

Imgur

變得最小的一組 a & b

梯度下降法

沒錯,這裡我們就是會用到梯度下降法

這裡複習一下,在梯度下降法中,我們會利用座標上某一點的梯度,來決定我們下一步要前進的方向。在這個例子當中,有兩個變數 ab,要計算梯度,需要分別計算 Loss function 對 a 和 b 的偏微分。

另外,我們需要設定一個起始點(一個開始走下山的點),以及一個 learning rate Imgur 來調整每一步跨出的步伐。

接下來,讓我們正式開始實作吧

梯度下降法 in Python

1. 引入 NumPy 與 Matplotlib

import matplotlib.pyplot as plt
import numpy as np

2. 建立數值陣列

x_data = [ 9,  4,  7 , 3 , 10  , 7 , 4 ,  3 , 1,  9]
y_data = [  45  , 24 ,  59  , 28  , 91 , 28  ,  42 ,  18 , 14, 82]

這裡的 x_data 就是油耗,y_data 就是里程數。同時,我們這裡假設模型為

# 假設模型為 y_data = a * x_data + b

3. 建立網格資料

這裡我們分別建立兩個一維等差陣列 XY,其範圍是 0 到 10,每隔 0.05 產生一個值。

接著,我們建立了一個二維陣列 Z ,來儲存每一個 X[n], Y[n] 位置上的損失函數。根據先前提過的損失函數算法,寫法為

(y_data[n] - b - a*x_data[n])**2

# 建立網格資料
x = np.arange(0,10,0.05)
y = np.arange(0,10,0.05)
Z =  np.zeros((len(x), len(y)))
X, Y = np.meshgrid(x, y)

for i in range(len(x)):
    for j in range(len(y)):
        b = x[i]
        a = y[j]
        Z[j][i] = 0
        for n in range(len(x_data)):
            Z[j][i] = Z[j][i] +  (y_data[n] - b - a*x_data[n])**2
        Z[j][i] = Z[j][i]/len(x_data)

4. 決定參數

這裡我們決定了 a 與 b 的起始點(從哪裡開始走),learning rate(每一步走多遠),iteration 次數(我們要走多少步),以及最後我們用兩個陣列,來分別儲存每一步我的走到的位置

# 決定 a 與 b 的起始點
b = 0
a = 0

# 決定 learning rate
lr = 0.00005

# 決定 iteration 的次數
iteration = 10000

# 儲存每一次 iterate 後的結果
b_history = [b]
a_history = [a]

5. 執行梯度下降

# 執行梯度下降
for i in range(iteration):
    b_grad = 0.0
    a_grad = 0.0
    
    # 計算損失函數分別對 a 和 b 的偏微分
    for n in range(len(x_data)):
        b_grad = b_grad  - 2.0*(y_data[n] - b - a*x_data[n])*1.0
        a_grad = a_grad  - 2.0*(y_data[n] - b - a*x_data[n])*x_data[n]
        
    # 更新 a, b 位置 
    b = b - lr * b_grad
    a = a - lr * a_grad
    
    # 紀錄 a, b 的位置 
    b_history.append(b)
    a_history.append(a)

6. 繪圖

# 建立等高線圖
plt.contourf(x,y,Z, 50, alpha=0.5, cmap=plt.get_cmap('jet'))

# 繪製目標點
plt.plot([1.97], [7.22], 'x', ms=12, markeredgewidth=3, color='red')

# 繪製 a,b iteration 的結果
plt.plot(b_history, a_history, 'o-', ms=3, lw=1.5, color='black')

# 定義圖形範圍
plt.xlim(0,5)
plt.ylim(0,10)

# 繪製 x 軸與 y 軸的標籤
plt.xlabel(r'$b$', fontsize=16)
plt.ylabel(r'$a$', fontsize=16)
plt.show()

這裡你可能會有個問題,我們還沒開始走,怎麼就知道目標點在哪裡呢?這個讓我明天再來說明

7. 結果出爐

Imgur

圖形上的每個黑點,都代表著我們的每一步。我們看到這些黑點從 b = 0 & a = 0(也就是圖形的原點)開始向 1 點鐘方向前進。根據我們對等高線的理解,這樣子切過一條又一條的等高線,不是在上升就是在下降。

當然這裡我們是不斷的下降高度。這裡我加上等高線的值,讓你可以更清楚的了解。

Imgur

然而走到某一個階段之後,有個轉彎,往另外一個方向前進。雖然圖形上不是很明顯,但實際上我們仍然不斷的下降當中。

那麼,最後我們走到哪裡呢?這裡讓我們印出走了一萬步之後,最後一步的位置

Imgur

(看起來其實距離目標點 1.97, 7.22 很接近了,但還是不夠接近呢)

也就是說,機器走到目前為止,認為 y = 7.2328846321328095 * x + 1.8477027996627977 是一條最好的線,能夠最好的「預測」油耗與里程之間的關係。這就是機器學習如何預測的過程!

小結

今天很快的示範了如何用 Python 來實作梯度下降法,但是過程中似乎還有許多問題需要討論,像是

  1. 我是怎麼知道目標點的?
  2. 怎麼知道要從哪裡開始?
  3. learning rate 要怎麼設定?
  4. 要怎麼減少 iteration 的數量
  5. 要怎麼知道自己走到的地方低不低呢?

... 等等的問題。讓我們用後續幾天的時間,來慢慢理解更多關於梯度下降法的事情!


上一篇
[Day 20] Hello Matplotlib! (2)
下一篇
[Day 22] 更多關於梯度下降法
系列文
Machine Learning for Dummies30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言